"use strict";

const utils = require("../utils");
const buildURL = require("../helpers/buildURL");
const InterceptorManager = require("./InterceptorManager");
const dispatchRequest = require("./dispatchRequest");
const mergeConfig = require("./mergeConfig");

/**
 * 创建一个axios实例
 *
 * @class
 * @param {Object} instanceConfig axios默认配置
 */
function Axios(instanceConfig) {
  this.defaults = instanceConfig;
  this.interceptors = {
    request: new InterceptorManager(),
    response: new InterceptorManager(),
  };
}

/**
 * 调度一个请求
 *
 * @param {...any} args 可变参数对象
 */
Axios.prototype.request = function request(...args) {
  // 特定于此请求的配置（与this.defaults合并）
  let config = {};
  if (utils.isString(args[0])) {
    config = args[1] || {};
    config.url = args[0];
  } else {
    config = args[0] || {};
  }

  config = mergeConfig(this.defaults, config);

  if (config.method) {
    config.method = config.method.toLowerCase();
  } else if (config.defaults.method) {
    config.method = config.defaults.method.toLowerCase();
  } else {
    config.method = "get";
  }

  // 拦截器核心实现逻辑
  const chain = [dispatchRequest, undefined];
  const promise = Promise.resolve(config);

  this.interceptors.request.forEach(function unshiftRequestInterceptors(
    interceptor
  ) {
    chain.unshift(interceptor.fulfilled, interceptor.rejected);
  });

  this.interceptors.response.forEach(function pushResponseInterceptors(
    interceptor
  ) {
    chain.push(interceptor.fulfilled, interceptor.rejected);
  });

  while (chain.length) {
    promise = promise.then(chain.shift(), chain.shift());
  }

  return promise;
};

Axios.prototype.getUri = function getUri(config) {
  config = mergeConfig(this.defaults, config);
  return buildURL(config.url, config.params, config.paramsSerializer).replace(
    /^\?/,
    ""
  );
};

/**
 * 为支持的请求方法设置别名
 */
utils.forEach(
  ["delete", "get", "head", "options"],
  function forEachMethodNoData(method) {
    Axios.prototype[method] = function (url, config = {}) {
      return this.request(
        utils.merge(config, {
          method: method,
          url: url,
        })
      );
    };
  }
);

utils.forEach(["post", "put", "patch"], function forEachMethodWithData(method) {
  Axios.prototype[method] = function (url, data, config = {}) {
    return this.request(
      utils.merge(config, {
        method: method,
        url: url,
        data: data,
      })
    );
  };
});

module.exports = Axios;
